package org.microframework.cloud.gateway.filter;

import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import reactor.util.context.Context;

import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

/**
 * 访问日志过滤器
 * 详细记录请求和响应信息，提供日志追踪功能
 */
@Slf4j
@Component
public class AccessLogFilter implements GlobalFilter, Ordered {

    // MDC中的键名常量
    private static final String TRACE_ID = "traceId";
    private static final String REQUEST_PATH = "path";
    private static final String REQUEST_METHOD = "method";
    private static final String CLIENT_IP = "clientIp";
    private static final String REQUEST_TIME = "requestTime";
    private static final String REQUEST_HEADERS = "X-Request-ID";

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        Instant startTime = Instant.now();
        ServerHttpRequest request = exchange.getRequest();
        
        // 生成追踪ID（优先使用上游服务传来的追踪ID）
        final String traceId;
        String headerTraceId = request.getHeaders().getFirst("X-Trace-ID");
        if (headerTraceId == null || headerTraceId.isEmpty()) {
            traceId = UUID.randomUUID().toString().replace("-", "");
        } else {
            traceId = headerTraceId;
        }
        
        // 生成请求ID
        String requestId = UUID.randomUUID().toString().replace("-", "");
        
        // 获取请求信息
        String method = request.getMethod().name();
        String path = request.getURI().getPath();
        String query = request.getURI().getQuery() != null ? request.getURI().getQuery() : "";
        final String clientIp = java.util.Optional.ofNullable(request.getRemoteAddress())
                .map(addr -> java.util.Optional.ofNullable(addr.getAddress())
                        .map(inetAddr -> inetAddr.getHostAddress())
                        .orElse("unknown"))
                .orElse("unknown");
        
        // 创建MDC上下文数据
        Map<String, String> contextMap = new HashMap<>();
        contextMap.put(TRACE_ID, traceId);
        contextMap.put(REQUEST_PATH, path);
        contextMap.put(REQUEST_METHOD, method);
        contextMap.put(CLIENT_IP, clientIp);
        contextMap.put(REQUEST_TIME, startTime.toString());
        contextMap.put(REQUEST_HEADERS, requestId);
        
        // 获取请求头信息（排除一些敏感信息）
        HttpHeaders headers = request.getHeaders();
        List<String> headerInfo = new ArrayList<>();
        headers.forEach((name, values) -> {
            if (!isHeaderSensitive(name)) {
                headerInfo.add(name + ": " + String.join(", ", values));
            }
        });
        
        // 使用MDC记录请求开始信息
        try (MDC.MDCCloseable closeable = MDC.putCloseable(TRACE_ID, traceId)) {
            MDC.put(REQUEST_PATH, path);
            MDC.put(REQUEST_METHOD, method);
            MDC.put(CLIENT_IP, clientIp);
            
            log.info("[TRACE-START] 请求开始 - TraceID: {}, 路径: {}, 方法: {}, 客户端IP: {}, 查询参数: {}",
                    traceId, path, method, clientIp, query);
            
            if (log.isDebugEnabled()) {
                log.debug("[TRACE-DETAIL] 请求头信息: {}", headerInfo);
            }
        }
        
        // 添加追踪ID和请求ID到请求和响应头中
        ServerHttpRequest mutatedRequest = request.mutate()
                .header("X-Trace-ID", traceId)
                .header("X-Request-ID", requestId)
                .build();
        
        ServerWebExchange mutatedExchange = exchange.mutate()
                .request(mutatedRequest)
                .build();
        
        // 使用Reactor上下文传递追踪信息
        return chain.filter(mutatedExchange)
                .contextWrite(Context.of("traceContext", contextMap))
                .doOnSuccess(v -> logResponse(mutatedExchange, traceId, requestId, startTime, path, method, clientIp))
                .doOnError(e -> logError(e, traceId, path, method, clientIp));
    }
    
    /**
     * 记录响应信息
     */
    private void logResponse(ServerWebExchange exchange, String traceId, String requestId, 
                           Instant startTime, String path, String method, String clientIp) {
        ServerHttpResponse response = exchange.getResponse();
        Duration duration = Duration.between(startTime, Instant.now());
        int statusCode = java.util.Optional.ofNullable(response.getStatusCode())
                .map(status -> status.value())
                .orElse(0);
        
        // 添加追踪ID和请求ID到响应头
        response.getHeaders().add("X-Trace-ID", traceId);
        response.getHeaders().add("X-Request-ID", requestId);
        
        // 使用MDC记录响应信息
        try (MDC.MDCCloseable closeable = MDC.putCloseable(TRACE_ID, traceId)) {
            MDC.put(REQUEST_PATH, path);
            MDC.put(REQUEST_METHOD, method);
            MDC.put(CLIENT_IP, clientIp);
            
            if (statusCode >= 400) {
                log.warn("[TRACE-END] 请求异常 - TraceID: {}, 状态码: {}, 耗时: {}ms, 路径: {}, 方法: {}, 客户端IP: {}",
                        traceId, statusCode, duration.toMillis(), path, method, clientIp);
            } else {
                log.info("[TRACE-END] 请求完成 - TraceID: {}, 状态码: {}, 耗时: {}ms, 路径: {}, 方法: {}, 客户端IP: {}",
                        traceId, statusCode, duration.toMillis(), path, method, clientIp);
            }
        }
    }
    
    /**
     * 记录错误信息
     */
    private void logError(Throwable error, String traceId, String path, String method, String clientIp) {
        try (MDC.MDCCloseable closeable = MDC.putCloseable(TRACE_ID, traceId)) {
            MDC.put(REQUEST_PATH, path);
            MDC.put(REQUEST_METHOD, method);
            MDC.put(CLIENT_IP, clientIp);
            
            log.error("[TRACE-ERROR] 请求处理错误 - TraceID: {}, 路径: {}, 方法: {}, 客户端IP: {}, 错误: {}",
                    traceId, path, method, clientIp, error.getMessage(), error);
        }
    }
    
    /**
     * 判断请求头是否敏感，不应记录到日志中
     */
    private boolean isHeaderSensitive(String headerName) {
        String name = headerName.toLowerCase();
        return name.contains("authorization") || 
               name.contains("cookie") || 
               name.contains("token") || 
               name.contains("password") || 
               name.contains("secret");
    }

    @Override
    public int getOrder() {
        // 在请求过滤器之后，响应过滤器之前
        return Ordered.HIGHEST_PRECEDENCE + 15;
    }
} 